We (Nishanth, Adboy, Abdulhadi and I) went go-karting on June 16th, 2019. It was lit. We thought we did pretty well. Here are some sobering visualizations.
Read the data
# TODO: update to read from image file, and populate data.table/csv directly
times <- fread("gokart-track-times-june-16-2019.csv")
head(times)
## Lap Number Nishanth Abdulhadi Aaruran Adboy
## 1: 1 27713 61982 34485 72429
## 2: 2 31973 24316 39092 81354
## 3: 3 22975 61277 34751 32978
## 4: 4 22641 29914 32485 31332
## 5: 5 23320 26863 28475 28715
## 6: 6 24012 27645 31672 27623
The times are in milliseconds. For example, Nishanth’s first lap was completed in 27.713 seconds.
times$`Lap Number` <- as.integer(times$`Lap Number`)
summary(times)
## Lap Number Nishanth Abdulhadi Aaruran Adboy
## Min. : 1 Min. :21091 Min. :21972 Min. :23122 Min. :24854
## 1st Qu.:10 1st Qu.:21598 1st Qu.:23416 1st Qu.:24743 1st Qu.:25755
## Median :19 Median :22233 Median :24110 Median :25612 Median :28116
## Mean :19 Mean :22926 Mean :26698 Mean :27334 Mean :35406
## 3rd Qu.:28 3rd Qu.:23569 3rd Qu.:24783 3rd Qu.:28318 3rd Qu.:32391
## Max. :37 Max. :31973 Max. :61982 Max. :39092 Max. :81354
## NA's :5 NA's :7 NA's :9
From this alone we can see the following: 1. Nish had the fastest lap 2. Nish had the most laps 3. Nish had the best median lap time, and average lap time.
Let’s see how the times change across the laps.
times.across.laps <- times %>% gather(Nishanth, Abdulhadi, Aaruran, Adboy,
key='Driver', value='Time')
times.across.laps$Time <- times.across.laps$Time / 1000
ggplot(times.across.laps, aes(x=`Lap Number`, y=Time, col=Driver)) + geom_point() + geom_line()
## Warning: Removed 21 rows containing missing values (geom_point).
## Warning: Removed 21 rows containing missing values (geom_path).
It’s pretty easy to see when crashes happened.
ggplot(times.across.laps, aes(x=`Lap Number`, y=Time)) + geom_point() + geom_line() + facet_wrap(Driver ~ .) + geom_smooth()
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## Warning: Removed 21 rows containing non-finite values (stat_smooth).
## Warning: Removed 21 rows containing missing values (geom_point).
By inspection, every lap over 40 seconds is a crash. However, there are a few ‘quick-fix’ crashes that are under this limit which are harder to distinguish from this data alone.
Let’s take a closer look at each person, instead of comparing them.
for(d in c('Nishanth', 'Adboy', 'Abdulhadi', 'Aaruran')) {
p <- ggplot(times.across.laps %>% filter(eval(Driver==d)), aes(x=`Lap Number`, y=Time)) + geom_point() + ggtitle(paste(d, '\'s Lap Times', sep="")) + geom_smooth()
print(p)
}
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## Warning: Removed 9 rows containing non-finite values (stat_smooth).
## Warning: Removed 9 rows containing missing values (geom_point).
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## Warning: Removed 5 rows containing non-finite values (stat_smooth).
## Warning: Removed 5 rows containing missing values (geom_point).
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## Warning: Removed 7 rows containing non-finite values (stat_smooth).
## Warning: Removed 7 rows containing missing values (geom_point).
I think what happened here is Nish had a lot of ‘fighting’ as he lapped us, so his times are a bit more sporadic, but still quite fast overall.
Aside from his outlier’s (crashes) Adboy appears to have improved marginally. He’d be a great racer if it wasn’t for all those pesky walls jumping in front of the kart.
Abdul had a few bad laps at the beginning but was quite consistent afterwards.
I (Aaruran) gradually pressed the accelerator harder and harder, and you can see after lap 25, I lost traction and my times went back up.
But who obtained the fastest lap speed, averaged across the lap ? We can safely assume the stop speed of the go-karts is 30km/h. Let’s also take Nish’s fastest lap and assume he had an average speed of 24 km/h (i.e. 80% of 30 km/h) on this lap. Nish’s fastest time was 21.091 seconds. We can use this to then estimate the track length at 140.613 metres. Use google to help you do conversions if you want to check my math.
convert_to_kmh <- function(x) {
return(x * 1e-3 * 3600)
}
ggplot(times.across.laps, aes(x=`Lap Number`, y=convert_to_kmh(140.613/Time), col=Driver)) + geom_point() + geom_line() + ylab('Average Speed (km/h)')
## Warning: Removed 21 rows containing missing values (geom_point).
## Warning: Removed 21 rows containing missing values (geom_path).
Notice that we gradually got faster and faster around the track, as we became more comfortable with the turns and the acceleration of the track.
cumulative.times <- times
cumulative.times <- na.omit(cumulative.times)
driver.names <- c('Nishanth', 'Adboy', 'Abdulhadi', 'Aaruran')
for (col in driver.names)
set(cumulative.times, j=col, value=as.double(cumulative.times[[col]] / 1000))
cumulative.times$Lap <- cumulative.times$`Lap Number`
cumulative.times$`Lap Number` <- NULL
cumulative.times <- cumulative.times[, Nishanth := cumsum(Nishanth)]
cumulative.times <- cumulative.times[, Adboy := cumsum(Adboy)]
cumulative.times <- cumulative.times[, Abdulhadi := cumsum(Abdulhadi)]
cumulative.times <- cumulative.times[, Aaruran := cumsum(Aaruran)]
Now, let’s see how the overtaking took place.
cumulative.times.melted <- cumulative.times %>% gather(Nishanth, Adboy, Abdulhadi, Aaruran, key=Driver, value=TimeStamp)
ggplot(cumulative.times.melted,aes(x=TimeStamp, y=Lap, col=Driver)) + geom_point() + geom_line() + xlab('Elapsed Time in Seconds')
We can see that Abdulhadi overtook me (Aaruran) right around the 18th lap.
p <- ggplot(cumulative.times.melted, aes(x=TimeStamp, y=Driver, col=Lap)) + geom_point() + ggtitle("Laps Completed")
ggplotly(p)
This plot is simple but also revealing. For instance, we have the humiliating knowledge that Nish finished lap 10 shortly after Adboy finished lap 5.
As well, we can see that Nish finished 15 laps in under 6 minutes. No wonder he knew that buying only 15 laps was a rip-off.
Adboy crashed a lot. Let’s do him a kindness and filter out all crashes, to compare overall lap times.
ggplot(times.across.laps %>% filter(Time <= 40), aes(x=`Lap Number`, y=Time)) + geom_point() + geom_smooth() + facet_wrap(Driver ~ .)
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
I (Aaruran) have a peculiar outlier, and I think that signifies one crash I had, which I managed to recover from without external help. I think I had crashed into somebody else.
Also, it’s clear that Adboy was able to improvise, adapt and overcome adversity around lap 20.
Interestingly, it looks like Nish had trouble very 8 laps or so. This was probably when we was fighting with someone else on the track (cough cough Kareem cough).
for(d in c('Nishanth', 'Adboy', 'Abdulhadi', 'Aaruran')) {
p <- ggplot(times.across.laps %>% filter(Time <= 40) %>%filter(eval(Driver==d)), aes(x=`Lap Number`, y=Time)) + geom_point() + ggtitle(paste(d, '\'s Lap Times, ignoring crashes', sep="")) + geom_smooth()
print(p)
}
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
Nish was far and away the best racer of our group. While I began in second place, Abdul was able to overcome his initially rocky start and edged a lead ahead of me. Adboy struggled with crashing but nonetheless showed improvement in his technique.
Also, the ordering of the final contenders more-or-less aligns with the experience we all have with real-life driving. Neat-o.